package wxpayHelper

import (
	"context"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/json"
	"errors"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/util/gconv"
	"github.com/shenghui0779/gochat"
	"github.com/shenghui0779/gochat/mp"
	"github.com/shenghui0779/gochat/oa"
)

// GetWxOa 获取微信公众号底层方法
func GetWxOa() *oa.OA {
	wxOa := gochat.NewOA(g.Cfg().GetString("pay.wechatpay.appId"), g.Cfg().GetString("pay.wechatpay.appSecret"))

	return wxOa
}

func GetWxMp() *mp.MP {
	wxMp := gochat.NewMP(g.Cfg().GetString("wechat.mp.appId"),
		g.Cfg().GetString("wechat.mp.appSecret"))

	return wxMp
}

func GetWxOaAccessToken(ctx context.Context) string {
	cacheKey := "wx_AccessToken"
	do, err := g.Redis().DoVar("GET", cacheKey)
	if err != nil {
		panic(err)
	}
	if do.IsEmpty() {
		wxoa := GetWxOa()
		token, err := wxoa.AccessToken(ctx)
		if err != nil {
			panic(err)
		}
		_, err = g.Redis().Do("SETEX", cacheKey, token.ExpiresIn-100, token.Token)
		if err != nil {
			panic(err)
		}

		return token.Token
	} else {
		return do.String()
	}
}

type JSSDKTicket struct {
	Errcode   int    `json:"errcode"`
	Errmsg    string `json:"errmsg"`
	Ticket    string `json:"ticket"`
	ExpiresIn int    `json:"expires_in"`
}

func GetJSSDKTicket(ctx context.Context) string {
	cacheKey := "wx_jsapiTicket"
	do, err := g.Redis().DoVar("GET", cacheKey)
	if err != nil {
		panic(err)
	}
	if do.IsEmpty() {
		accessToken := GetWxOaAccessToken(ctx)
		var jsapiTicket *JSSDKTicket
		err := g.Client().GetVar("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + accessToken + "&type=jsapi").
			Scan(&jsapiTicket)
		if err != nil {
			panic(err)
		}

		if jsapiTicket.Errcode != 0 {
			panic("获取 ticket 失败：" + jsapiTicket.Errmsg)
		}

		_, err = g.Redis().Do("SETEX", cacheKey, jsapiTicket.ExpiresIn-100, jsapiTicket.Ticket)
		if err != nil {
			panic(err)
		}

		return jsapiTicket.Ticket
	} else {
		return do.String()
	}
}

func GetJSSDKSign(ctx context.Context, url string) g.Map {
	jsapiTicket := GetJSSDKTicket(ctx)
	wxoa := GetWxOa()
	jsData := gconv.Map(wxoa.JSSDKSign(jsapiTicket, url))
	jsData["appid"] = wxoa.AppID()
	return jsData
}

func GetAuthUser(ctx context.Context, accessToken, openId string) (*oa.AuthUser, error) {
	var wechatInfo *oa.AuthUser
	err := g.Client().Ctx(ctx).GetVar("https://api.weixin.qq.com/sns/userinfo?access_token=" +
		accessToken + "&openid=" + openId + "&lang=zh_CN").Scan(&wechatInfo)
	if err != nil {
		return nil, err
	}

	return wechatInfo, nil
}

var (
	ErrAppIDNotMatch       = errors.New("app id not match")
	ErrInvalidBlockSize    = errors.New("invalid block size")
	ErrInvalidPKCS7Data    = errors.New("invalid PKCS7 data")
	ErrInvalidPKCS7Padding = errors.New("invalid padding on input")
)

// pkcs7Unpad returns slice of the original data without padding
func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
	if blockSize <= 0 {
		return nil, ErrInvalidBlockSize
	}
	if len(data)%blockSize != 0 || len(data) == 0 {
		return nil, ErrInvalidPKCS7Data
	}
	c := data[len(data)-1]
	n := int(c)
	if n == 0 || n > len(data) {
		return nil, ErrInvalidPKCS7Padding
	}
	for i := 0; i < n; i++ {
		if data[len(data)-n+i] != c {
			return nil, ErrInvalidPKCS7Padding
		}
	}
	return data[:len(data)-n], nil
}

func DecryptAuthInfo(sessionKey, iv, encryptedData string) (*mp.UserInfo, error) {
	/*var dest mp.UserInfo
	key, err := base64.StdEncoding.DecodeString(sessionKey)
	if err != nil {
		return nil, fmt.Errorf("sessionKey DecodeString err " + err.Error())
	}

	ivb, err := base64.StdEncoding.DecodeString(iv)

	if err != nil {
		return nil, fmt.Errorf("iv DecodeString err " + err.Error())
	}

	cipherText, err := base64.StdEncoding.DecodeString(encryptedData)

	if err != nil {
		return nil, fmt.Errorf("encryptedData DecodeString err " + err.Error())
	}

	cbc := wx.NewCBCCrypto(key, ivb, wx.PKCS7)

	b, err := cbc.Decrypt(cipherText)

	if err != nil {
		return nil, fmt.Errorf("Decrypt err " + err.Error())
	}

	if err = json.Unmarshal(b, &dest); err != nil {
		g.Dump(string(b))
		return nil, fmt.Errorf("Unmarshal err " + err.Error())
	}*/

	/*if dest.AppID() != mp.appid {
		return nil,fmt.Errorf("appid mismatch, want: %s, got: %s", mp.appid, dest.AppID())
	}*/

	aesKey, err := base64.StdEncoding.DecodeString(sessionKey)
	if err != nil {
		return nil, err
	}
	cipherText, err := base64.StdEncoding.DecodeString(encryptedData)
	if err != nil {
		return nil, err
	}
	ivBytes, err := base64.StdEncoding.DecodeString(iv)
	if err != nil {
		return nil, err
	}

	block, err := aes.NewCipher(aesKey)
	if err != nil {
		return nil, err
	}
	mode := cipher.NewCBCDecrypter(block, ivBytes)
	mode.CryptBlocks(cipherText, cipherText)
	cipherText, err = pkcs7Unpad(cipherText, block.BlockSize())
	if err != nil {
		return nil, err
	}
	var dest mp.UserInfo
	err = json.Unmarshal(cipherText, &dest)
	if err != nil {
		return nil, err
	}
	/*if userInfo.Watermark.AppID != w.appID {
		return nil, ErrAppIDNotMatch
	}*/
	return &dest, nil

	//return &dest, nil
}
